package org.oos;

import java.io.Serializable;
import java.util.Collections;
import java.util.Map;
import java.util.logging.Logger;

import org.oos.atom.entries.ContactEntry;
import org.oos.atom.entries.ReviewEntry;
import org.oos.atom.entries.SiteEntry;
import org.oos.atom.entries.UserEntry;
import org.oos.auth.AuthenticationHandler;
import org.oos.dao.ContactDao;
import org.oos.dao.Dao;
import org.oos.dao.MultimediaDao;
import org.oos.dao.ReviewDao;
import org.oos.dao.SiteDao;
import org.oos.dao.SocialActivityDao;
import org.oos.dao.UserActivityDao;
import org.oos.dao.UserCheckinDao;
import org.oos.dao.UserDao;
import org.oos.dao.UserListsDao;
import org.oos.dao.UserSiteDao;
import org.oos.dao.UserTagsDao;
import org.oos.dao.exceptions.ReadException;
import org.oos.dao.impl.ContactDaoImpl;
import org.oos.dao.impl.MultimediaDaoImpl;
import org.oos.dao.impl.ReviewDaoImpl;
import org.oos.dao.impl.SiteDaoImpl;
import org.oos.dao.impl.SocialActivityDaoImpl;
import org.oos.dao.impl.UserActivityDaoImpl;
import org.oos.dao.impl.UserCheckinDaoImpl;
import org.oos.dao.impl.UserDaoImpl;
import org.oos.dao.impl.UserListsDaoImpl;
import org.oos.dao.impl.UserSiteDaoImpl;
import org.oos.dao.impl.UserTagsDaoImpl;
import org.oos.model.Contact;
import org.oos.model.Review;
import org.oos.model.Site;
import org.oos.model.User;
import org.oos.util.Cache;

/**
 * 
 * This is the entry point of the 11870.com API and all uses of this library
 * should start with one of the factory methods provided by this class.
 * 
 * It provides with two Factory methods to create either a {@link SiteProvider}
 * or a {@link SecureServiceProvider}.
 * 
 * @author pablo.gonzalez@11870.com
 * 
 */
public class OOS4Java implements Serializable {
	private static final long serialVersionUID = -5092984821344132652L;

	// set up the namespace dictionary
	static {
		Constants.OOS_NAMESPACE_DICTIONARY.addNamespace("os",
				"http://a9.com/-/spec/opensearch/1.1/");
		Constants.OOS_NAMESPACE_DICTIONARY.addNamespace("oos",
				"http://11870.com/api/oos");
		Constants.OOS_NAMESPACE_DICTIONARY.addNamespace("app",
				"http://www.w3.org/2007/app");
		Constants.OOS_NAMESPACE_DICTIONARY.addNamespace("georss",
				"http://www.georss.org/georss");
		Constants.OOS_NAMESPACE_DICTIONARY.addNamespace("atom",
				"http://www.w3.org/2005/Atom");
		Constants.OOS_NAMESPACE_DICTIONARY.addNamespace("activity",
				"http://activitystrea.ms/spec/1.0/");
		Constants.OOS_NAMESPACE_DICTIONARY.addNamespace("",
				"http://www.w3.org/2005/Atom");
	}
	
	public static final Map<String, User> USERS_CACHE = Collections
		.synchronizedMap(new Cache<String, User>());
	
	public static final Map<String, Site> SITES_CACHE = Collections
		.synchronizedMap(new Cache<String, Site>());
	
	private Logger log;
	
	private AuthenticationHandler authenticationHandler;
	
	private User me;
	
	public OOS4Java setLog(Logger log) {
		this.log = log;
		return this;
	}
	
	public OOS4Java setAuthenticationHandler(AuthenticationHandler authHandler) {
		this.authenticationHandler = authHandler;
		return this;
	}

	public SiteDao getSiteDao() {
		return init(new SiteDaoImpl());
	}

	public ReviewDao getReviewDao() {
		return init(new ReviewDaoImpl());
	}
	
	public MultimediaDao getMultimediaDao() {
		return init(new MultimediaDaoImpl());
	}
	
	public UserDao getUserDao() {
		return init(new UserDaoImpl());
	}
	
	public UserSiteDao getUserSiteDao() {
		return init(new UserSiteDaoImpl());
	}
	
	public ContactDao getContactDao() {
		return init(new ContactDaoImpl());
	}

	public UserActivityDao getUserActivityDao() {
		return init(new UserActivityDaoImpl());
	}

	public SocialActivityDao getSocialActivityDao() {
		return init(new SocialActivityDaoImpl());
	}

	public UserCheckinDao getUserCheckinDao() {
		return init(new UserCheckinDaoImpl());
	}

	public UserListsDao getUserListsDao() {
		return init(new UserListsDaoImpl());
	}

	public UserTagsDao getUserTagsDao() {
		return init(new UserTagsDaoImpl());
	}

	public User getMe() throws ReadException {
		if (!isAuthenticated()) {
			return null;
		}
		
		if (me == null) {
			UserDao dao = getUserDao();
			UserEntry entry = dao.read(0);
			me = new User(this, entry);
			USERS_CACHE.put(me.getSlug(), me);
		}
		return me;
	}
	
	public void setMe(User user) {
		this.me = user;
		USERS_CACHE.put(me.getSlug(), me);
	}
	
	public void resetMe() {
		this.me = null;
	}
	
	public void resetUser(String slug) {
		if (me != null && me.getSlug().equals(slug)) {
			me = null;
		}
		
		USERS_CACHE.remove(slug);
	}
	
	public User getUser(String userSlug) throws ReadException {
		if (isAuthenticated() && getMe().getSlug().equals(userSlug)) {
			return me;
		}
		
		User user;
		if (USERS_CACHE.containsKey(userSlug)) {
			user = USERS_CACHE.get(userSlug);
		} else {
			UserDao dao = getUserDao();
			UserEntry entry = dao.readEntry(userSlug);
			user = new User(this, entry);
			USERS_CACHE.put(userSlug, user);
		}
		return user;
	}
	
	public Site getSite(String siteSlug) throws ReadException {
		Site site;
		if (SITES_CACHE.containsKey(siteSlug)) {
			site = SITES_CACHE.get(siteSlug);
		} else {
			ReviewDao dao = getReviewDao();
			dao.setSiteSlug(siteSlug);
			SiteEntry entry = dao.read(0);
			site = new Site(this, entry);
			SITES_CACHE.put(siteSlug, site);
		}
		return site;
	}
	
	public Contact getContact(String userSlug, String contactSlug) throws ReadException {
		if (userSlug.equals(contactSlug)) {
			return null;
		}

		ContactDao dao = getContactDao();
		dao.setUserSlug(userSlug);
		ContactEntry entry = dao.readEntry(contactSlug);
		return entry == null? null : new Contact(this, entry);
	}
	
	public Review getReview(String userSlug, String siteSlug) throws ReadException {
		UserSiteDao dao = getUserSiteDao();
		dao.setUserSlug(userSlug);
		ReviewEntry entry = dao.readEntry(siteSlug);
		return entry == null? null : new Review(this, entry);
	}
	
	public boolean isAuthenticated() {
		return ! authenticationHandler.isAnonymous();
	}
	
	public boolean isAuthorized(User user) throws ReadException {
		return this.isAuthenticated() && this.getMe().getSlug().equals(user.getSlug());
	}

	private <D extends Dao<?, ?>> D init(D dao) {
		dao.setLog(log);
		dao.setAuthenticationHandler(authenticationHandler);
		return dao;
	}
}
